# error codes:
# 1 - timeout 
# 2 -  tcp errors
# 3 - dns error. can't resolve name and etc. (see EV::ADNS)
# 4- open socket count overflow
# 5 - reserved fo future use
# 6 - something wrong with proxy server ( response code not equal 200 or it unexpectedly closed connection)
package Net::Coro::EV::Connect::HTTPProxy;
use common::sense;
our $VERSION = 0.01;

use EV;
use Net::Coro::EV::Connect::Socket;

our $TRACE = 0;
our $TIMEOUT = 20;

sub new {
	my ($class, $sckhost, $sckport, $socket) =@_;
	
	my $this ={};
	
	$socket = new Net::Coro::EV::Connect::Socket if !$socket;
	$this->{_SOCKET} = $socket;

	$this->{_SCKHOST} = $sckhost; # proxy  host
	$this->{_SCKPORT} = $sckport; # proxy  port
	$this->{_TIMEOUT} = $TIMEOUT;
	
	bless $this, $class;
	return $this;
}
sub type {'http_proxy'}
sub child {
	my ($this) = @_;
	my $child = {};
	bless $child, ref $this;
		
	while (my ($key, $val) = each(%$this)) {
		$child->{$key} = $val;
	}
	$child->{_SOCKET} = $this->{_SOCKET}->child;
	delete $child->{_CALLER};
	return $child;
}

sub auth {
	my ($this, $name, $pass) = @_;
	$this->{_NAME} = $name; # proxy  auth name
	$this->{_PASS} = $pass; # sock auth pass
	
	# to do, example
	# Proxy-Authorization: Negotiate TlRMTVNTUAABAAAAB4IAAAAAAAAAAAAAAAAAAAAFAAAADw=="
}

sub address {
	my ($this, $sckhost, $sckport) = @_;
	if (defined $sckhost) {
		$this->{_SCKHOST} = $sckhost; # socks host
		$this->{_SCKPORT} = $sckport; # socks port
	}
	return ($this->{_SCKHOST}, $this->{_SCKPORT});
}

sub connect {
	my ($this, $host, $port, $caller) = @_;
	die("error: proxy already connected ($host:$port)") if defined $this->{_WIO};
	$this->{_CALLER} = $caller;
	
	$this->{_HOST} = $host;
	$this->{_PORT} = $port;
	
	print "TRACE $this->{_ID}, Proxy connection begining - $this->{_SCKHOST}:$this->{_SCKPORT}\n" if $TRACE;
	$this->{_SOCKET}->connect($this->{_SCKHOST}, $this->{_SCKPORT}, $this);
}

sub onconnect {
	my ($this, $wio, $wtm, $serv_ip, $serv_port) = @_;
	return $this->err($this->{_SOCKET}->errm, $this->{_SOCKET}->errc) if !$wio;
	
	$this->callback($wio, $wtm, $this->{_HOST}, $this->{_PORT});
}


######## COMMON ARCHITECTURAL METHODS AND EVENT HANDLERS ###################
sub onwritten {
	my ($this, $w) = @_;
	$w->events (EV::READ);
}

sub onclose {
	my ($this) = @_;
	# make desigion what to do
	# default is to send error
	$this->err("http_proxy: connectnion closed by peer",6);
}

sub onbreak {
	my ($this, $msg, $code) = @_;
	# make desigion what to do
	# default is to send error
	$this->err($msg, 2);
}

sub callback {
	my ($this, @resp) = @_;
	undef $this->{_ERRM};
	undef $this->{_ERRC};
	print "TRACE $this->{_ID}, Proxy connection done \n" if $TRACE;
	(delete $this->{_CALLER})->onconnect(@resp);
}

sub err {
	my ($this, $msg, $code) = @_;
	$this->{_ERRM} = $msg;
	$this->{_ERRC} = $code;

	print "TRACE $this->{_ID}, Proxy connection error - $msg (code $code)\n" if $TRACE;
	$this->close;
	(delete $this->{_CALLER})->onconnect();
}


sub close {
	my ($this) =@_;
#	$this->{_STATE} = SCK_NONE;
	
	my $tm = delete $this->{_WTM};
	$tm->stop() if defined $tm;
	
	my $w = delete $this->{_WIO};
	if (defined $w) {
		$w->stop();
		my $fh = $w->fh;
		shutdown($fh, 2);
		CORE::close($fh);
	}
}

sub stop {
	my ($this) = @_;
	$this->{_SOCKET}->stop();
	$this->close();
	delete $this->{_CALLER};
}

############ COMMON PROPIRTIES ##############
sub errc {$_[0]->{_ERRC}}
sub errm {$_[0]->{_ERRM}}

sub timeout {
	my ($this, $val) = @_;	
	if (defined $val) {
		$this->{_TIMEOUT} = $val;
		$this->{_SOCKET}->timeout($val);
	}
	return $this->{_TIMEOUT};
}
sub bindip {$_[0]->{_SOCKET}->bindip($_[1])}

sub id {
	my ($this, $id) = @_;
	if (defined $id) {
		$this->{_ID} = $id;
		$this->{_SOCKET}->id($id);
	}
	$this->{_ID};
}
1;